/*
 * Scanner for CS 3240 Spring '11 Final Project
 * Nathaniel Rowe
 */

/*
 *Basic workings:
 *Classes and Tokens:
   *Keywords
     *BEGIN 'begin'
     *END 'end'
     *PRINT 'print'
     *READ 'read'
   *Other static
     *SEMICOLON ';'
     *COMMA ','
     *LEFTPAR '('
     *RIGHTPAR ')'
     *PLUS '+'
     *MINUS '-'
     *MULTIPLY '*'
     *MODULO '%'
     *ASSIGN ':='
   *Variable named
     *ID -start with any letter or underscore, len < 11, contains a-Z,0-9,_
       Underscore must be followed by letter or digit
     *INTNUM -unsigned int, no leading zeros
 *Given input file:
   *Read each line into a string
   *Split string using strtok/equivalent
     *Tokenize split string based on class
     *Add token to the tokenized string
     *Write string to output file
 */
#include <iostream>
#include <fstream>
#include <string>
#include <vector>

using namespace std;

#define DEBUG 1

class Scanner{
  ifstream code; // Input code
  ofstream tokenized; // Tokenized output
  vector<string> in_tokens;
  
public:
  int SetFiles(const char *, const char *);
  void TokenizeInput();
  void Classify(string);
  bool CharIsAlpha(char);
  bool CharIsNumeric(char);
  ~Scanner();
};

bool Scanner::CharIsNumeric(char c){
  if(c > 47 && c <58){
    return true;
  }
  return false;
}

bool Scanner::CharIsAlpha(char c){
  if((c > 64 && c < 91) || (c > 96 && c < 123)){
    return true;
  }
  return false;
}

Scanner::~Scanner(){
  code.close();
  tokenized.close();
}

void Scanner::Classify(string s){
  string out;

  if(!s.compare(" ")){
    return;
  }
  if(s.size()){
    if(!(s.compare("begin"))){
      out = string("BEGIN");
    }else if(!(s.compare("end"))){
      out = string("END");
    }else if(!(s.compare("print"))){
      out = string("PRINT");
    }else if(!(s.compare("read"))){
      out = string("READ");
    }else if(!(s.compare(";"))){//Now do operators
      out = string("SEMICOLON");
    }else if(!(s.compare(","))){
      out = string("COMMA");
    }else if(!(s.compare("("))){
      out = string("LEFTPAR");
    }else if(!(s.compare(")"))){
      out = string("RIGHTPAR");
    }else if(!(s.compare("+"))){
      out = string("PLUS");
    }else if(!(s.compare("-"))){
      out = string("MINUS");
    }else if(!(s.compare("*"))){
      out = string("MULTIPLY");
    }else if(!(s.compare("%"))){
      out = string("MODULO");
    }else if(!(s.compare(":="))){
      out = string("ASSIGN");
    }else{//should be ID or INTNUM
      out = string("(ID or INTNUM)");
      char c = s[0];
      size_t j=0;
      bool well_formed = false;
      if(CharIsNumeric(c)){
	//continue check
	for(j=0;j<s.size();j++){
	  c=s[j];
	  if(CharIsNumeric(c)){
	    well_formed=true;
	  }else{
	    well_formed=false;
	    j=s.size();
	  }
	}
	if(well_formed){
	  out = string("INTNUM");
	}else{
	  out = string("!UNIDENTIFIED_CLASS!");
	}
      }else if(CharIsAlpha(c) || c=='_'){
	//contine check
	for(j=0;j<s.size();j++){
	  c=s[j];
	  if(CharIsNumeric(c)||CharIsAlpha(c)){
	    well_formed=true;
	  }else if(c=='_'){
	    well_formed=well_formed;
	  }else{
	    well_formed=false;
	    j=s.size();
	  }
	}
	if(well_formed && j < 11){
	  out=string("ID");
	}else{
	  out=string("!UNIDENTIFIED_CLASS!");
	}
      }
    }
    tokenized << out << " ";
  }
}

int Scanner::SetFiles(const char *code_name, const char *tokenized_name){
  code.open(code_name, ifstream::in);
  tokenized.open(tokenized_name, ofstream::out);
  if(code.fail()){
    cout << "Failed to open " << code_name << "\n";
    return -1;
  }
  if(tokenized.fail()){
    cout << "Failed to open " << tokenized_name << "\n";
    return -2;
  }

  return 0;
}

/*
 *Read each line into in_tokens
 *Per item in in_tokens
   *Add each to split_in, (split on spaces)
   *After all added, for each in split_in
     *If end with ';', split into two and convert based on class
     *If not ending with ';', convert based on class
     *Write to file
   *Write newline to file
 */

void Scanner::TokenizeInput(){

  string s;

  //Read lines of code into in_tokens
  while(code.good()){
    getline(code, s);
    in_tokens.push_back(s);
  }

  for(size_t i = 0; i < in_tokens.size();i++){
    s = in_tokens[i];
    string token;

    for(size_t j=0; j<s.size();j++){
      if(s[j]==';' ||
	 s[j]==',' ||
	 s[j]=='(' ||
	 s[j]==')' ||
	 s[j]=='+' ||
	 s[j]=='-' ||
	 s[j]=='*' ||
	 s[j]==' ' ||
	 s[j]=='%' ){
	Classify(token);
	token=string(1,s[j]);
	Classify(token);
	token.clear();
      }else if(s[j]=='='){
	token+=s[j];
	Classify(token);
	token.clear();
      }else if(s[j]==':'){
	Classify(token);
	token=string(1,s[j]);
      }else{
	token+=s[j];
      }
    }
    Classify(token);
    tokenized << endl;
  }
}

int main(){
  Scanner s;
  s.SetFiles("code.txt", "tokens.txt");
  s.TokenizeInput();

  return 0;
}
